package service

import (
	v1 "SmartLibrary/api/v1"
	"SmartLibrary/internal/model"
	"SmartLibrary/internal/repository"
	"SmartLibrary/pkg/jwt"
	"context"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"math"
	"time"
)

type BookAdminService interface {
	LoginBookAdmin(ctx context.Context, req *v1.BookAdminLoginRequest) (string, error)
	//GetBookAdmin 返回管理员信息
	GetBookAdmin(ctx context.Context, id *string) (*v1.BookAdminInfoResponseData, error)
	BookBorrow(ctx context.Context, req *v1.BookBorrowRequest) error
	GetBookMessage(ctx context.Context, bookNumber int64) error
	//BookReturn 归还图书
	BookReturn(ctx context.Context, req *v1.BookReturnRequest) error
	//获取借书报表
	BorrowReport(ctx context.Context, req *v1.BorrowReportRequest) (*v1.BorrowReportResponseTotalData, error)
	//获取归还报表
	ReturnReport(ctx context.Context, req *v1.ReturnReportRequest) (*v1.ReturnReportResponseTotalData, error)
	//获取公告信息
	GetNotice(ctx context.Context, req *v1.GetBookMessageRequest) (*v1.GetBookMessageResponseTotalData, error)
	//添加新公告
	AddNotice(ctx context.Context, req *v1.NoticeRequest) error
	// DeleteNotice 删除公告
	DeleteNotice(ctx context.Context, noticeId int64) error
	// GetNoticeBookAdmin 获取指定id 的公告
	GetNoticeBookAdmin(ctx context.Context, req *v1.GetNoticeRequest) (*v1.GetBookMessageResponseData, error)
	// UpdateNotice 修改已更改的公告
	UpdateNotice(ctx context.Context, req *v1.UpdateNoticeRequest) error
}

var cst = time.FixedZone("CST", 8*3600)

func NewBookAdminService(
	service *Service,
	bookAdminRepository repository.BookAdminRepository,
) BookAdminService {
	return &bookAdminService{
		Service:             service,
		bookAdminRepository: bookAdminRepository,
	}
}

type bookAdminService struct {
	*Service
	bookAdminRepository repository.BookAdminRepository
}

//	func (s *bookAdminService) GetBookAdmin(ctx context.Context, id int64) (*model.UserBookAdmin, error) {
//		return s.bookAdminRepository.GetBookAdmin(ctx, id)
//	}
func (s *bookAdminService) LoginBookAdmin(ctx context.Context, req *v1.BookAdminLoginRequest) (string, error) {
	admin, err := s.bookAdminRepository.LoginBookAdmin(ctx, req.UserName)
	//根据返回的model.UserBookAdmin判断是否登录成功
	if err != nil {
		s.logger.Error("login failed", zap.Error(err))
		return "", v1.ErrInternalServerError
	}
	if admin == nil {
		return "", v1.ErrUserIdNotFound
	}
	//比较输入的密码和数据库中的密码是否一致
	err = bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(req.Password))
	if err != nil {
		return "", v1.ErrBookAdminPassword
	}
	token, err := s.jwt.GenToken(admin.Id, time.Now().Add(time.Hour*24*7*30))
	if err != nil {
		s.logger.Error("gen token failed", zap.Error(err))
		return "", v1.ErrInternalServerError
	}
	return token, nil
}
func (s *bookAdminService) GetBookAdmin(ctx context.Context, id *string) (*v1.BookAdminInfoResponseData, error) {
	//通过id查询管理员信息
	admin, err := s.bookAdminRepository.GetBookAdmin(ctx, id)
	if err != nil {
		s.logger.Error("get book admin failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	return &v1.BookAdminInfoResponseData{
		BookAdminId:   int(admin.Id),
		BookAdminName: admin.BookAdminName,
		Status:        admin.Status,
		Email:         admin.Email,
		CreateTime:    admin.CreateTime,
		UpdateTime:    admin.UpdateTime,
		Username:      admin.Username,
	}, nil
}
func (s *bookAdminService) BookBorrow(ctx context.Context, req *v1.BookBorrowRequest) error {
	//检查借阅号是否正确
	user, err := s.bookAdminRepository.CheckCardNumber(ctx, req.CardNumber)
	if err != nil {
		s.logger.Error("check card number failed", zap.Error(err))
		if err.Error() == "record not found" {
			return v1.ErrCardNumber
		}
		return v1.ErrInternalServerError
	}
	//判断用户是否满足借阅规则
	//获取用户使用的借阅规则的具体信息
	rule, err := s.bookAdminRepository.GetRule(ctx, user.RuleNumber)
	if err != nil {
		s.logger.Error("get rule failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//查看用户的借阅还没还的书总数
	borrow, err := s.bookAdminRepository.GetBookBorrow(ctx, req.CardNumber)
	if err != nil {
		s.logger.Error("get book borrow failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//通过判断还书日期是否为空，来判断用户是否还书
	num := 0
	if len(*borrow) > 0 {
		for _, v := range *borrow {
			if v.ReturnDate.IsZero() {
				num++
			}
		}
	}
	if num >= rule.BookLimitNumber {
		return v1.ErrBookBorrowLimit
	}
	//将信息存入借阅库
	borrowDate, err := time.ParseInLocation("2006-01-02 15:04:05", req.BorrowDate, cst)
	if err != nil {
		s.logger.Error("parse time failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//归还日期需要设定初始值
	returnDate, err := time.Parse("2006-01-02 15:04:05", "2001-01-01 00:00:00")
	if err != nil {
		s.logger.Error("parse time failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//计算截至日期
	closeDate := borrowDate.AddDate(0, 0, rule.BookLimitNumber)
	var book = &model.TBooksBorrow{
		BookNumber: req.BookNumber,
		BorrowDate: borrowDate,
		CardNumber: req.CardNumber,
		CloseDate:  closeDate,
		CreateTime: time.Now().In(cst),
		UpdateTime: time.Now().In(cst),
		ReturnDate: returnDate,
	}
	err = s.bookAdminRepository.CreateBookBorrow(ctx, book)
	if err != nil {
		s.logger.Error("create book borrow failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	return nil
}

var ch = make(chan model.TBooksBorrow, 2)

func (s *bookAdminService) GetBookMessage(ctx context.Context, bookNumber int64) error {
	//通过信息查询书是否存在
	books, err := s.bookAdminRepository.GetBookMessage(ctx, "book_number", bookNumber)
	if err != nil {
		s.logger.Error("get book message failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	if len(books) == 0 {
		return v1.ErrBookNumber
	}
	//通过判断是否逾期
	if books[0].ReturnDate.IsZero() {
		if books[0].CloseDate.Before(time.Now().In(cst)) {
			//没有逾期
			return nil
		} else {
			return v1.ErrOverdue
		}
	}
	//通过通道传输数据
	return nil
}
func (s *bookAdminService) BookReturn(ctx context.Context, req *v1.BookReturnRequest) error {
	//更新数据库表
	//更新归还时间
	err := s.bookAdminRepository.ReturnBook(ctx, req)
	if err != nil {
		s.logger.Error("return book failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//接受上个管道中的数据
	//book := <-ch
	t, err := time.ParseInLocation("2006-01-02 15:04:05", req.ReturnDate, cst)
	if err != nil {
		s.logger.Error("parse time failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//从jwt中获取用户i
	claims := jwt.MyCustomClaims{
		UserId: ctx.Value("claims").(*jwt.MyCustomClaims).UserId,
	}
	book, err := s.bookAdminRepository.GetBookMessage(ctx, "book_number", req.BookNumber)
	if err != nil {
		s.logger.Error("get book message failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	//进行违章信息创建
	violation := &model.TViolation{
		BookNumber:       req.BookNumber,
		ViolationMessage: req.ViolationMessage,
		CardNumber:       req.CardNumber,
		ViolationAdminID: claims.UserId,
		BorrowDate:       book[0].BorrowDate,
		CloseDate:        book[0].CloseDate,
		ReturnDate:       t,
		CreateTime:       time.Now().In(cst),
		UpdateTime:       time.Now().In(cst),
	}
	err = s.bookAdminRepository.CreateViolation(ctx, violation)
	if err != nil {
		s.logger.Error("create violation failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	return nil
}

func (s *bookAdminService) BorrowReport(ctx context.Context, req *v1.BorrowReportRequest) (*v1.BorrowReportResponseTotalData, error) {
	//通过违章信息表查看借书报表
	books, err := s.bookAdminRepository.FindViolation(ctx, req)
	if err != nil {
		s.logger.Error("find violation failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	//获取所有的借阅信息
	total, err := s.bookAdminRepository.GetAllBookBorrow(ctx)
	if err != nil {
		s.logger.Error("get all book borrow failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	borrowReportResponseData := make([]v1.BorrowReportResponseData, len(*books))
	//计算预期天数
	for k, v := range *books {
		if v.CloseDate.Sub(v.BorrowDate) < 0 {
			borrowReportResponseData[k].ExpireDays = int64(v.BorrowDate.Sub(v.CloseDate)) / 24
		}
		//通过管理员id查询到管理员的用户名
		name, err := s.bookAdminRepository.GetBookAdminName(ctx, v.ViolationAdminID)
		if err != nil {
			s.logger.Error("get book admin name failed", zap.Error(err))
			return nil, v1.ErrInternalServerError
		}
		borrowReportResponseData[k].BookNumber = v.BookNumber
		borrowReportResponseData[k].BorrowDate = v.BorrowDate
		borrowReportResponseData[k].CardNumber = v.CardNumber
		borrowReportResponseData[k].CloseDate = v.CloseDate
		borrowReportResponseData[k].CreateTime = v.CreateTime
		borrowReportResponseData[k].UpdateTime = v.UpdateTime
		borrowReportResponseData[k].ReturnDate = v.ReturnDate
		borrowReportResponseData[k].ViolationAdmin = name
		borrowReportResponseData[k].ViolationAdminId = v.ViolationAdminID
		borrowReportResponseData[k].ViolationId = v.ViolationID
		borrowReportResponseData[k].ViolationMessage = v.ViolationMessage
	}
	borrowReportResponse := &v1.BorrowReportResponseTotalData{
		Records: borrowReportResponseData,
		Total:   int64(total),
		Size:    int64(req.Size),
		Current: int64(req.PageNum),
		Pages:   int64(math.Ceil(float64(total) / float64(req.Size))),
	}
	if total == 0 {
		borrowReportResponse.Pages = 0
	}
	return borrowReportResponse, nil
}
func (s *bookAdminService) ReturnReport(ctx context.Context, req *v1.ReturnReportRequest) (*v1.ReturnReportResponseTotalData, error) {
	//查找所有还书日期为null的数据
	books, err := s.bookAdminRepository.ReturnReport(ctx, req)
	if err != nil {
		s.logger.Error("return report failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	returnReportResponseData := make([]v1.ReturnReportResponseData, len(*books))
	if len(*books) > 0 {
		for k, v := range *books {
			returnReportResponseData[k].BookNumber = int(v.BookNumber)
			returnReportResponseData[k].BorrowDate = v.BorrowDate
			returnReportResponseData[k].BorrowId = v.BorrowID
			returnReportResponseData[k].CardNumber = v.CardNumber
			returnReportResponseData[k].CloseDate = v.CloseDate
			returnReportResponseData[k].CreateTime = v.CreateTime
			returnReportResponseData[k].ReturnDate = v.ReturnDate
			returnReportResponseData[k].UpdateTime = v.UpdateTime
		}
	}
	//获取所有的还书信息
	total, err := s.bookAdminRepository.GetAllReturnBook(ctx)
	if err != nil {
		s.logger.Error("get all return book failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	returnReportResponse := &v1.ReturnReportResponseTotalData{
		Records: returnReportResponseData,
		Total:   int64(total),
		Size:    int64(req.Size),
		Current: int64(req.PageNum),
		Pages:   int64(math.Ceil(float64(total) / float64(req.Size))),
	}
	if total == 0 {
		returnReportResponse.Pages = 0
	}
	return returnReportResponse, nil
}
func (s *bookAdminService) GetNotice(ctx context.Context, req *v1.GetBookMessageRequest) (*v1.GetBookMessageResponseTotalData, error) {
	notice, err := s.bookAdminRepository.GetNotice(ctx, req)
	if err != nil {
		s.logger.Error("get notice failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	getBookMessageResponse := make([]v1.GetBookMessageResponseData, len(*notice))
	if len(*notice) > 0 {
		for k, v := range *notice {
			getBookMessageResponse[k].CreateTime = v.CreateTime
			getBookMessageResponse[k].NoticeAdminId = int(v.NoticeAdminID)
			getBookMessageResponse[k].NoticeContent = v.NoticeContent
			getBookMessageResponse[k].NoticeId = v.NoticeID
			getBookMessageResponse[k].NoticeTitle = v.NoticeTitle
			getBookMessageResponse[k].UpdateTime = v.UpdateTime
		}
	}
	//获取所有公告信息
	total, err := s.bookAdminRepository.GetAllNotice(ctx)
	if err != nil {
		s.logger.Error("get all notice failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	getBookMessageResponseTotalData := &v1.GetBookMessageResponseTotalData{
		Records: getBookMessageResponse,
		Total:   int64(total),
		Size:    int64(req.PageSize),
		Current: int64(req.PageNum),
		Pages:   int64(math.Ceil(float64(total) / float64(req.PageSize))),
	}
	if total == 0 {
		getBookMessageResponseTotalData.Pages = 0
	}
	return getBookMessageResponseTotalData, nil
}

func (s *bookAdminService) AddNotice(ctx context.Context, req *v1.NoticeRequest) error {
	//添加新公告
	var notice = &model.Notice{}
	notice.NoticeAdminID = req.BookAdminId
	notice.NoticeContent = req.NoticeContent
	notice.NoticeTitle = req.NoticeTitle
	notice.CreateTime = time.Now().In(cst)
	notice.UpdateTime = time.Now().In(cst)
	err := s.bookAdminRepository.CreateNotice(ctx, notice)
	if err != nil {
		s.logger.Error("create notice failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	return nil
}

// DeleteNotice 删除公告
func (s *bookAdminService) DeleteNotice(ctx context.Context, id int64) error {
	err := s.bookAdminRepository.DeleteNotice(ctx, id)
	if err != nil {
		s.logger.Error("delete notice failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	return nil
}

func (s *bookAdminService) GetNoticeBookAdmin(ctx context.Context, req *v1.GetNoticeRequest) (*v1.GetBookMessageResponseData, error) {
	notice, err := s.bookAdminRepository.GetNoticeById(ctx, req.NoticeId)
	if err != nil {
		s.logger.Error("get notice failed", zap.Error(err))
		return nil, v1.ErrInternalServerError
	}
	getBookMessageResponse := v1.GetBookMessageResponseData{
		CreateTime:    notice.CreateTime,
		NoticeAdminId: int(notice.NoticeAdminID),
		NoticeContent: notice.NoticeContent,
		NoticeId:      notice.NoticeID,
		NoticeTitle:   notice.NoticeTitle,
		UpdateTime:    notice.UpdateTime,
	}
	return &getBookMessageResponse, nil
}

// UpdateNotice 修改已更改的公告
func (s *bookAdminService) UpdateNotice(ctx context.Context, req *v1.UpdateNoticeRequest) error {
	err := s.bookAdminRepository.UpdateNotice(ctx, req)
	if err != nil {
		s.logger.Error("update notice failed", zap.Error(err))
		return v1.ErrInternalServerError
	}
	return nil
}
